(()=>{
  // 1、类 class 是一个模板，可以通过这个模板实例化对象,ts中需要做初始化的属性必须明确声明(或在构造函数中使用readonly修饰)，js则会自动声明
   class Person{
     name:string
     age:number
    //  构造函数，为了实例化对象的时候，可以直接对属性的值进行初始化
     constructor(name:string,age:number,sex:string){
      this.name = name
      this.age = age
      // this.sex = sex  //错误
     }
     run(){
       console.log('不动')
     }
   }

  // 2、继承  extends Person：基类   Teacher：子类
  class Teacher extends Person{
    // 自己独有属性
    xueke:string
    constructor(name:string,age:number,sex:string,xueke:string){
      // 初始化基类属性
      super(name,age,sex)
      // 初始化独有属性
      this.xueke = xueke
    }
    // 重写基类方法,不重写就是使用基类中run方法的逻辑
    run(): void {
      console.log('走')
      super.run()
    }
    // 独有方法
    teach(){
      console.log('讲课')
    }
  }
    class Student extends Person{
      // 自己独有属性
      xueke:string
      constructor(name:string,age:number,sex:string,xueke:string){
        // 初始化基类属性
        super(name,age,sex)
        // 初始化独有属性
        this.xueke = xueke
      }
      // 重写基类方法,不重写就是使用基类中run方法的逻辑
      run(): void {
        console.log('翘课')
        super.run()
      }
      // 独有方法
      teach(){
        console.log('讲课')
      }
    }
  // let p = new Person('T',12,'男')
  // p.run()//跑
  // let t = new Teacher('L',30,'女','语文')
  // t.run()//不重写：跑，重写后：走
  // t.teach()//讲课

  // 3、多态 父类型的引用指向子类的对象，不同类型的对象针对相同的方法，以使此方法产生不同的行为
  let s1:Person = new Student('zs',11,'男','体育')
  let t1:Person = new Teacher('ls',26,'女','政治')
  function fun(agme:Person):void{
    agme.run()
  }
  // fun(s1)
  // fun(t1)

  // 4、修饰符 public:公共（任何地方） private:私有（只能在定义的类中使用） protected：受保护（只能在定义的类中和其子类中使用）
  // readonly:只读（无访问权限，一旦初始化完毕，任何地方不可对其更改）多用在构造函数参数修饰，这样就不必在Dog类中声明name属性，可以把声明和赋值合并至一处
  class Dog{
    // public name:string
    // private name:string
    // protected name:string
    constructor(readonly name:string){
      this.name = name
    }
    run(){
      console.log('跑的贼六~',this.name)
    }
  }
  class DH extends Dog{
    constructor(name:string){
      super(name)
    }
    run(): void {
      console.log(this.name) //基类中的name 定义为private时这里会报错
    }
  }
  let d:Dog = new Dog('大黄')
  // d.name = '555' //基类中name修饰为readonly时，无法分配到 "name" ，因为它是只读属性
  // console.log(d.name) //基类中的name 定义为private和protected时这里会报错

  // 5、存取器 通过 getters/setters 来截取对对象成员的访问。 它能帮助你有效的控制对对象成员的访问
  class Doctor{
    firstName:string
    lastName:string
    static a:string = '秘制小汉堡~'
    constructor(firstName:string,lastName:string){
      this.firstName = firstName
      this.lastName = lastName
    }
    get fullName(){
      return this.firstName + '-' + this.lastName
    }
    set fullName(value){
      const names = value.split('-')
      this.firstName = names[0]
      this.lastName = names[1]
    }
  }
  let doc = new Doctor('liu','护士长')
  // console.log(doc.fullName)
  // doc.fullName = 'liu-院长'
  // console.log(doc.lastName)

  // 6、静态属性 static修饰的属性为静态属性，区别去一般属性存在于实例对象上，而静态属性存在于类本身
  // 获取Doctor类静态属性a
  // let a1 = Doctor.a
  // console.log(a1)
  // Doctor.a = '老八专属~'
  // console.log(Doctor.a)

  // 7、抽象类 abstract 作为其他子类的基类使用，抽象类无法实例化，区别于接口，抽象类可以包含成员的实现细节
  // abstract 关键字是用于定义抽象类和在抽象类内部定义抽象方法。抽象类中也可以有实例方法和抽象属性（很少使用抽象属性）
  abstract class Animal{
    // 抽象方法 子类必须实现
    abstract walk()
    // 实例方法 子类可直接使用
    eat(){
      console.log('动物们都得干饭')
    }
  }
  class Pig extends Animal{
    // 实现抽象方法
    walk() {
      console.log('猪在地上走')
    }
    // 重写方法
    eat(): void {
      console.log('猪大口吃')
    }
  }
  class Bird extends Animal{
    // 实现抽象方法
    walk() {
      console.log('鸟在天上飞')
    }
    // 重写方法
    eat(): void {
      console.log('鸟小口吃')
    }
  }
  // 顺便复习一下多态 父类型的引用指向子类的对象，不同类型的对象针对相同的方法，以使此方法产生不同的行为
  let pig:Animal = new Pig()
  let bird:Animal = new Bird()
  // pig.walk()
  // bird.walk()
  // pig.eat()
  // bird.eat()
})()